package com.marvin.springBoot.repository.impl;

import com.marvin.springBoot.repository.IBaseRepository;
import com.marvin.springBoot.repository.support.Query;
import com.marvin.springBoot.util.Page;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Selection;
import javax.persistence.metamodel.EntityType;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * @author marvin.ma
 * @create 2017-09-20 23:09
 * @desc IBaseDao接口实现了BaseDaoImpl类
 **/
@Repository
public class BaseRepositoryImpl implements IBaseRepository {

    private static Logger logger = LoggerFactory.getLogger(BaseRepositoryImpl.class);
    /** 每次批量操作数 */
    private int batchSize = 50;

    /** 设置每次操作数 */
    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }

    /**
     * JAP entityManager 持久化上下文
     */
    @PersistenceContext
    private EntityManager entityManager;

    /**
     * 通过类创建查询条件
     * @param clazz
     * @return
     */
    @Override
    public Query forClass(Class clazz) {
        Query query = new Query();
        query.setClazz(clazz);
        query.setEntityManager(this.entityManager);
        query.setCriteriaBuilder(this.entityManager.getCriteriaBuilder());
        query.setCriteriaQuery(this.entityManager.getCriteriaBuilder().createQuery(clazz));
        query.setFrom(query.getCriteriaQuery().from(clazz));
        query.setPredicates(new ArrayList<>());
        query.setOrders(new ArrayList<>());
        return query;
    }

    /**
     * 通过id查询
     * @param clazz
     * @param id
     * @param <E>
     * @return
     */
    @Override
    public <E> E queryById(Class clazz, Serializable id) {
        return (E) entityManager.find(clazz, id);
    }

    /**
     * 插入记录
     *
     * @param entity
     *            要插入的记录
     */
    public <E> void insert(E entity) {
        entityManager.persist(entity);
    }

    /**
     * 批量增加
     *
     * @param list
     *            要新增的数据
     */
    @Override
    public void insertList(List list) {
        if (list == null || list.size() == 0) {
            return;
        }
        int i = 0;
        for (Object o : list) {
            this.insert(o);
            if (i % batchSize == 0) {
                entityManager.flush();
            }
            i++;
        }
    }

    /**
     * 更新记录
     *
     * @param entity
     *            要更新的记录
     */
    @Override
    public <E> void update(E entity) {
        if (entity instanceof List) {
            this.updateList((List) entity);
            return;
        }
        entityManager.merge(entity);
    }

    /**
     * 批量更新
     */
    @Override
    public void updateList(List list) {
        for (Object entity : list) {
            this.update(entity);
        }
    }

    /**
     * 删除单条记录
     *
     * @param entity
     *    要删除的记录
     */
    @Override
    public <E> void delete(E entity) {
        entityManager.remove(entity);
    }

    /**
     * 删除单条记录
     *
     * @param entity
     *    要删除的记录
     */
    @Override
    public void delete(Class entity, String jpqlCondition) {
        if (StringUtils.isBlank(jpqlCondition)) {
            jpqlCondition = "1=1";
        }
        int no = updateJpql("delete " + entity.getName() + " where " + jpqlCondition);
        logger.debug(entity.getName() + "删除" + no + "条数据");
    }

    /**
     * 批量删除记录
     *
     * @param list 要删除的记录
     */
    @Override
    public <E> void deleteList(List<E> list) {
        int i = 0;
        if (list instanceof List) {
            for (Object o : list) {
                this.delete(o);
                i ++;
            }
        }
    }

    /**
     * 根据ids删除数据
     *
     * @param entity
     *            删除实体类
     * @param ids
     *            删除条件
     */
    @Override
    public void deleteByIdList(Class entity, List ids) {
        String idName = getIdName(entity, entityManager);
        StringBuffer sb = new StringBuffer();
        sb.append(idName + " in(");
        for (int i = 0; i < ids.size(); i++) {
            sb.append("'" + ids.get(i) + "',");
        }
        String jpqlCondition = sb.substring(0, sb.length() - 1) + ")";
        delete(entity, jpqlCondition);
    }

    /**
     * 执行更新操作的sql语句
     * @param sql
     * @return
     */
    @Override
    public Integer updateSql(String sql) {
        return entityManager.createNativeQuery(sql).executeUpdate();
    }

    /**
     * 执行更新操作的jpql语句
     * @param jpql
     * @return
     */
    @Override
    public Integer updateJpql(String jpql) {
        return entityManager.createQuery(jpql).executeUpdate();
    }

    /**
     * 统计记录
     *
     * @param Query
     *            统计条件
     */
    @Override
    public Long getCount(Query Query) {
        Selection selection = Query.getCriteriaQuery().getSelection();
        Query.getCriteriaQuery().select(Query.getCriteriaBuilder().count(Query.getFrom()));
        Long count = (Long) entityManager.createQuery(Query.newCriteriaQuery()).getResultList().get(0);
        Query.getCriteriaQuery().select(selection);
        return count;
    }

    /**
     * 统计记录
     * @param jpql 统计sql
     * @return
     */
    @Override
    public Long getCount(String jpql) {
        return (Long) entityManager.createQuery(jpql).getResultList().get(0);
    }

    /***
     *
     * @Method updateJpql
     * @Description 根据传入的带有占位符的sql语句, 做增删改操作 例如
     *              updateJpql("update user t set t.name=? where t.id=?"
     *              ,{[zx],[23]})
     * @Date 2012-8-9 下午3:38:35
     * @param jpql
     *            占位符式的sql
     * @param paramList
     *            list里面装有[zx, 23]
     */
    @Override
    public void updateJpql(String jpql, List paramList) {
        javax.persistence.Query query = entityManager.createQuery(jpql);
        for (int i = 0; i < paramList.size(); i++) {
            query.setParameter(i + 1, paramList.get(i));
        }
        query.executeUpdate();
    }

    /**
     * 执行更新操作的jpql语句
     *
     * @param jpql
     *            要执行的jpql语句
     */
    @Override
    public <E> List<E> query(String jpql) {
        return entityManager.createQuery(jpql).getResultList();
    }

    /**
     * 执行更新操作的jpql语句实现分页查询
     * @param jpql
     * @param pageNo
     * @param rowsPerPage
     * @return
     */
    @Override
    public Page queryPageByJpql(String jpql, int pageNo, int rowsPerPage) {
        if (pageNo <= 0)
            pageNo = 1;
        if (rowsPerPage <= 0)
            rowsPerPage = 7;
        logger.debug("-----开始查询,页码:" + pageNo + ",每页显示:" + rowsPerPage + "----");

        String countJpql = "select count(*) from (" + jpql + ")";
        int count = getCount(countJpql).intValue();

        // 当把最后一页数据删除以后,页码会停留在最后一个上必须减一
        int totalPageCount = count / rowsPerPage;
        if (pageNo > totalPageCount && (count % rowsPerPage == 0)) {
            pageNo = totalPageCount;
        }
        if (pageNo - totalPageCount > 2) {
            pageNo = totalPageCount + 1;
        }
        int firstResult = (pageNo - 1) * rowsPerPage;
        if (firstResult < 0) {
            firstResult = 0;
        }
        List result = entityManager.createQuery(jpql).setFirstResult(firstResult).setMaxResults(rowsPerPage)
                .getResultList();
        return new Page(count, pageNo, rowsPerPage, result);
    }

    /**
     * 分页执行查询操作的jpql语句
     * @param jpql
     * @param firstResult 起始行
     * @param maxResults 结束行
     * @param <E>
     * @return
     */
    @Override
    public <E> List<E> query(String jpql, int firstResult, int maxResults) {
        List result = entityManager.createQuery(jpql).setFirstResult(firstResult).setMaxResults(maxResults)
                .getResultList();
        return result;
    }

    /**
     * 执行查询操作的sql语句
     *
     * @param sql
     *            要执行的sql语句
     */
    @Override
    public <E> List<E> queryBySql(String sql) {
        return entityManager.createNativeQuery(sql).getResultList();
    }

    /**
     * 分页执行查询操作的sql语句
     * @param sql
     * @param firstResult 起始行
     * @param maxResults 结束行
     * @param <E>
     * @return
     */
    @Override
    public <E> List<E> queryBySql(String sql, int firstResult, int maxResults) {
        return entityManager.createNativeQuery(sql).setFirstResult(firstResult).setMaxResults(maxResults)
                .getResultList();
    }

    /**
     * 根据query查找记录
     *
     * @param Query
     *            查询条件
     */
    @Override
    public <E> List<E> query(Query Query) {
        return entityManager.createQuery(Query.newCriteriaQuery()).getResultList();
    }

    /**
     * 分页查询
     *
     * @param Query
     *            查询条件
     * @param pageNo
     *            页号
     * @param rowsPerPage
     *            每页显示条数
     */
    @Override
    public Page queryPage(Query Query, int pageNo, int rowsPerPage) {
        if (pageNo <= 0)
            pageNo = 1;
        if (rowsPerPage <= 0)
            rowsPerPage = 7;
        logger.debug(Query.getClazz() + "-----开始查询,页码:" + pageNo + ",每页显示:" + rowsPerPage + "----");
        logger.debug("查询条件:");
        for (Predicate cri : Query.getPredicates())
            logger.debug(cri.toString());

        int count = getCount(Query).intValue();

        // 当把最后一页数据删除以后,页码会停留在最后一个上必须减一
        int totalPageCount = count / rowsPerPage;
        if (pageNo > totalPageCount && (count % rowsPerPage == 0)) {
            pageNo = totalPageCount;
        }
        if (pageNo - totalPageCount > 2) {
            pageNo = totalPageCount + 1;
        }
        int firstResult = (pageNo - 1) * rowsPerPage;
        if (firstResult < 0) {
            firstResult = 0;
        }
        List result = entityManager.createQuery(Query.newCriteriaQuery()).setFirstResult(firstResult)
                .setMaxResults(rowsPerPage).getResultList();
        return new Page(count, pageNo, rowsPerPage, result);
    }

    /**
     * 根据query查找记录
     *
     * @param Query
     *            查询条件
     * @param firstResult
     *            起始行
     * @param maxResults
     *            结束行
     */
    @Override
    public <E> List<E> query(Query Query, int firstResult, int maxResults) {
        List result = entityManager.createQuery(Query.newCriteriaQuery()).setFirstResult(firstResult)
                .setMaxResults(maxResults).getResultList();
        return result;
    }

    /**
     * 查询记录
     *
     * @param clazz
     *            要查询的实体类
     * @param hqlCondition
     *            查询条件
     */
    @Override
    public <E> List<E> query(Class clazz, String hqlCondition) {
        return entityManager.createQuery("select t from " + clazz.getName() + " as t where " + hqlCondition)
                .getResultList();
    }

    /**
     * 查询全部
     * @param clazz
     * @param <E>
     * @return
     */
    @Override
    public <E> List<E> queryAll(Class clazz) {
        CriteriaQuery criteriaQuery = entityManager.getCriteriaBuilder().createQuery(clazz);
        criteriaQuery.from(clazz);
        return entityManager.createQuery(criteriaQuery).getResultList();
    }

    /**
     * 获得主键名称
     *
     * @param clazz
     *            操作是实体对象
     * @param entityManager
     *            jpa的entityManager工厂
     * @return 初建名称
     * */
    public static String getIdName(Class clazz, EntityManager entityManager) {
        EntityType entityType = entityManager.getMetamodel().entity(clazz);
        return entityType.getId(entityType.getIdType().getJavaType()).getName();
    }

    public EntityManager getEntityManager() {
        return entityManager;
    }
}
